package cn.anecansaitin.industrialization_expansion;

import cn.anecansaitin.industrialization_expansion.client.ClientEventHooks;
import cn.anecansaitin.industrialization_expansion.common.CommonEventHooks;
import cn.anecansaitin.industrialization_expansion.common.ToolModuleType;
import cn.anecansaitin.industrialization_expansion.common.TranslationText;
import cn.anecansaitin.industrialization_expansion.common.item.CommonModifiedTool;
import cn.anecansaitin.industrialization_expansion.common.item.CommonToolModule;
import cn.anecansaitin.industrialization_expansion.common.item.crafting.LazyIngredientSerializer;
import cn.anecansaitin.industrialization_expansion.common.util.JsonFileReader;
import com.google.common.collect.Lists;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.ibm.icu.impl.Pair;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.enchantment.EnchantmentType;
import net.minecraft.item.Item;
import net.minecraft.util.JSONUtils;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.ToolType;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.registries.ForgeRegistries;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class ExtensionLoader {
    private static final Logger LOGGER = LogManager.getLogger();
    public static final List<Item> MODULES = new ArrayList<>();
    public static final List<String> MODULE_IDS = new ArrayList<>();
    public static final List<Item> TOOLS = new ArrayList<>();
    public static final List<String> TOOL_IDS = new ArrayList<>();
    public static final List<String> MODEL_IDS = new ArrayList<>();

    public static void load() {
        readCustomItemFile();
        DistExecutor.safeRunWhenOn(Dist.CLIENT, () -> ClientExtensionLoader::new);
    }

    /**
     * 读取用于物品注册的json文件
     */
    private static void readCustomItemFile() {
        File root = ModConstant.EXPAND_FILE.toFile();

        if (!root.exists() || !root.isDirectory()) {
            try {
                Files.createDirectory(ModConstant.EXPAND_FILE);
                Files.createDirectory(ModConstant.EXPAND_FILE.resolve("module"));
                Files.createDirectory(ModConstant.EXPAND_FILE.resolve("tool_type"));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            return;
        }

        Path modulePath = ModConstant.EXPAND_FILE.resolve("module");
        File moduleFile = modulePath.toFile();

        //读取模块
        if (!moduleFile.isDirectory()) {
            try {
                Files.createDirectory(modulePath);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else
            readModulesInDirectory(moduleFile);

        Path toolTypePath = ModConstant.EXPAND_FILE.resolve("tool_type");
        File toolTypeFile = toolTypePath.toFile();

        //读取工具
        if (!toolTypeFile.exists() || !moduleFile.isDirectory()) {
            try {
                Files.createDirectory(toolTypePath);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else
            readToolTypeInDirectory(toolTypeFile);
    }

    /**
     * @param file 工具模块的json所在父目录 <br/>
     *             从指定的文件目录读取其下所有的json文件并实例 <br/>
     *             化item
     */
    private static void readModulesInDirectory(File file) {
        File[] files = file.listFiles();

        if (files == null)
            return;

        for (File listFile : files) {
            if (listFile.isDirectory())
                readModulesInDirectory(listFile);
            else {
                JsonObject json = JsonFileReader.read(listFile);

                if (json == null) {
                    continue;
                }

                if (!JSONUtils.isStringValue(json, "id")) {
                    LOGGER.error("A module's id not defined: {}", listFile);
                    continue;
                }

                String id = JSONUtils.getAsString(json, "id");

                //读取模块类型
                if (!JSONUtils.isStringValue(json, "moduleType")) {
                    LOGGER.error("A module's type not defined: {}", listFile);
                    continue;
                }

                String moduleType = JSONUtils.getAsString(json, "moduleType");
                ToolModuleType toolModuleType = ToolModuleType.get(moduleType);
                List<Pair<ToolType, Integer>> toolTypeList = Lists.newArrayList();

                //读取工具类型
                if (JSONUtils.isArrayNode(json, "toolType")) {
                    JsonArray toolTypes = JSONUtils.getAsJsonArray(json, "toolType");

                    for (JsonElement element : toolTypes) {
                        if (element.isJsonObject()) {
                            JsonObject object = element.getAsJsonObject();

                            if (JSONUtils.isStringValue(object, "type")) {
                                toolTypeList.add(Pair.of(ToolType.get(JSONUtils.getAsString(object, "type")), JSONUtils.getAsInt(object, "level", 1)));
                                continue;
                            }
                        }

                        LOGGER.error("A module's tool type defined error: {}", listFile);
                    }
                }

                //读取附魔类型
                HashSet<EnchantmentType> enchantmentTypes = new HashSet<>();

                if (JSONUtils.isArrayNode(json, "enchantmentTypes")) {
                    for (JsonElement element : JSONUtils.getAsJsonArray(json, "enchantmentTypes")) {
                        try {
                            enchantmentTypes.add(EnchantmentType.valueOf(element.getAsString()));
                        } catch (IllegalArgumentException e) {
                            LOGGER.error("A module's enchantment types defined error: {}", listFile);
                        }
                    }
                }

                //读取附魔
                HashSet<Enchantment> enchantments = new HashSet<>();

                if (JSONUtils.isArrayNode(json, "enchantments")) {
                    for (JsonElement element : JSONUtils.getAsJsonArray(json, "enchantments")) {
                        Enchantment enchantment = ForgeRegistries.ENCHANTMENTS.getValue(new ResourceLocation(element.getAsString()));

                        if (enchantment == null)
                            LOGGER.error("A module's enchantments defined error: {}", listFile);
                        else
                            enchantments.add(enchantment);
                    }
                }

                Item moduleItem = new CommonToolModule(CommonToolModule.ModuleProperty
                        .crate()
                        .moduleType(toolModuleType)
                        .toolType(toolTypeList)
                        .modelName(JSONUtils.getAsString(json, "modelId", "miss"))
                        .renderColor(JSONUtils.getAsInt(json, "color", 0))
                        .energy(JSONUtils.getAsInt(json, "energy", 0))
                        .durability(JSONUtils.getAsInt(json, "durability", 0))
                        .destroySpeed(JSONUtils.getAsFloat(json, "destroySpeed", 0))
                        .fireResistant(JSONUtils.getAsBoolean(json, "fireResistant", false))
                        .damage(JSONUtils.getAsFloat(json, "damage", 0))
                        .attackSpeed(JSONUtils.getAsFloat(json, "attackSpeed", 0))
                        .xpRepairRatio(JSONUtils.getAsFloat(json, "xpRepairRatio", 0))
                        .material(LazyIngredientSerializer.INSISTENCE.parse(json.get("material").getAsJsonObject()))
                        .icon(JSONUtils.getAsString(json, "icon", "miss"))
                        .disabledShield(JSONUtils.getAsBoolean(json, "disabledShield", false))
                        .enchantability(JSONUtils.getAsInt(json, "enchantability", 0))
                        .enchantmentTypes(enchantmentTypes)
                        .enchantable(enchantments));

                MODULES.add(moduleItem);
                MODULE_IDS.add(id);
            }
        }
    }

    private static void readToolTypeInDirectory(File file) {
        File[] files = file.listFiles();

        if (files == null)
            return;

        for (File listFile : files) {
            if (listFile.isDirectory())
                readToolTypeInDirectory(listFile);
            else {
                JsonObject json = JsonFileReader.read(listFile);

                if (json == null)
                    continue;

                if (!JSONUtils.isStringValue(json, "id")) {
                    LOGGER.error("A tool type not defined id: {}", listFile);
                    continue;
                }

                String id = JSONUtils.getAsString(json, "id");
                int energyRatio = JSONUtils.getAsInt(json, "energyRatio", 0);
                String icon = JSONUtils.getAsString(json, "icon", "item_handle");
                ArrayList<ITextComponent> list = new ArrayList<>();
                JsonArray tips = JSONUtils.getAsJsonArray(json, "tip", new JsonArray());

                if (tips != null)
                    tips.forEach(obj -> list.add(TranslationText.getToolTip(obj.getAsString()).withStyle(TextFormatting.GRAY)));

                CommonModifiedTool tool = new CommonModifiedTool(id, energyRatio, list, icon);
                TOOLS.add(tool);
                TOOL_IDS.add(id);
            }
        }
    }

    /**
     * 读取需要额外加载的模型文件
     */
    public static void readCustomModelFile() {
        File root = ModConstant.EXPAND_FILE.toFile();

        if (!root.exists() || !root.isDirectory()) {
            try {
                Files.createDirectory(ModConstant.EXPAND_FILE);
                Files.createFile(ModConstant.EXPAND_FILE.resolve("model.json"));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            return;
        }

        Path jsonPath = ModConstant.EXPAND_FILE.resolve("model.json");
        File jsonFile = jsonPath.toFile();

        if (!jsonFile.isFile()) {
            try {
                Files.createFile(jsonPath);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            return;
        }

        JsonObject json = JsonFileReader.read(jsonFile);

        if (json == null || !JSONUtils.isArrayNode(json, "model"))
            return;

        JSONUtils.getAsJsonArray(json, "model").getAsJsonArray().forEach(element -> MODEL_IDS.add(element.getAsString()));
    }
}
